package com.linkhub.oms.admin.modules.stock.service.impl;

import com.google.common.collect.Lists;
import com.linkhub.oms.admin.common.exception.ExceptionFactory;
import com.linkhub.oms.admin.modules.stock.domain.dto.StockCheckDTO;
import com.linkhub.oms.admin.modules.stock.domain.dto.StockTransferDTO;
import com.linkhub.oms.admin.modules.stock.enums.BusinessType;
import com.linkhub.oms.admin.modules.stock.enums.ProductType;
import com.linkhub.oms.common.enums.IEnumConvert;
import com.linkhub.oms.common.enums.mp.CustomerOrderStatusEnum;
import com.linkhub.oms.common.enums.mp.EmployeeOrderStatusEnum;
import com.linkhub.oms.common.enums.mp.OrderStockStatusEnum;
import com.linkhub.oms.common.util.CacheKeyAssist;
import com.linkhub.oms.common.util.QtBeanUtil;
import com.linkhub.oms.common.util.json.QtFastjsonUtil;
import com.linkhub.oms.component.redis.locker.DistributedLocker;
import com.linkhub.oms.dao.mapper.CheckStockEntityMapper;
import com.linkhub.oms.dao.mapper.TransferStockEntityMapper;
import com.linkhub.oms.dao.mpservice.IOrderService;
import com.linkhub.oms.dao.mpservice.IPhysicalStockFlowService;
import com.linkhub.oms.dao.mpservice.IProductStockService;
import com.linkhub.oms.entity.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;

/**
 * @ClassName StockMaintainBizServiceImpl
 * @Description 库存维护
 * @Author XieYu
 * @Date 2024/7/13 23:04
 * @Version 1.0
 */
@Slf4j
@Service
public class StockMaintainBizServiceImpl {
    @Resource
    private IProductStockService productStockService;
    @Resource
    private TransferStockEntityMapper transferStockMapper;
    @Resource
    private CheckStockEntityMapper checkStockMapper;
    @Resource
    private OutStockBizServiceImpl outStockBizService;
    @Resource
    private DistributedLocker distributedLocker;
    @Resource
    private BatchBizServiceImpl batchBizService;
    @Resource
    private PreemptionStockBizServiceImpl preemptionStockBizService;
    @Resource
    private IPhysicalStockFlowService physicalStockFlowService;
    @Resource
    private IOrderService orderService;

    @Transactional(rollbackFor = Exception.class)
    public void wmsStockCheck(StockCheckDTO stockCheckDTO) {

        int skuQuantity = stockCheckDTO.getSkuQuantity();
        if (skuQuantity == 0) {
            log.info("库存盘点：变更数量为0，不需要做任何操作");
            return;
        }
        String lockKey = CacheKeyAssist.getPhysicalStockUpdatedLockKey(stockCheckDTO.getUserId(),
                stockCheckDTO.getSkuId(), stockCheckDTO.getStashName());
        if (!distributedLocker.tryLock(lockKey)) {
            log.error("库存盘点更新实物库存时获取锁失败");
            throw ExceptionFactory.getI18nBizException("stock.physical.updated.concurrence.error");
        }

        try {
            CheckStockEntity checkStockEntity = new CheckStockEntity();
            QtBeanUtil.copyProperties(stockCheckDTO, checkStockEntity);
            checkStockEntity.setAmount(stockCheckDTO.getSkuQuantity());
            ProductStockEntity productStock = productStockService.get(stockCheckDTO.getUserId(),
                    stockCheckDTO.getStashName(), stockCheckDTO.getSkuId());
            ProductType productType = IEnumConvert.valueOf(stockCheckDTO.getType(), ProductType.class);
            PhysicalStockFlowEntity srcFlow = new PhysicalStockFlowEntity();
            srcFlow.setBusinessType(BusinessType.STOCK_CHECK.getCode());
            srcFlow.setUserId(stockCheckDTO.getUserId());
            srcFlow.setSkuId(stockCheckDTO.getSkuId());
            srcFlow.setStashName(stockCheckDTO.getStashName());
            srcFlow.setBusinessNo(stockCheckDTO.getBusinessNo());
            srcFlow.setType(productType.getValue());
            checkStockEntity.setType(productType.getValue());
            List<PhysicalStockFlowEntity> flows = Lists.newArrayList();
            int outAmount = 0;
            if (productType == ProductType.BAD) {
                if (skuQuantity < 0 && Math.abs(skuQuantity) > productStock.getBadStock()) {
                    log.warn("库存盘点：盘亏残次品({})超过剩余实际({})的残次品数，请核对", skuQuantity, productStock.getBadStock());
                    throw ExceptionFactory.getI18nBizException("stock.check.error");
                }
                int srcBadStock = productStock.getBadStock();
                checkStockEntity.setBeforeAmount(productStock.getBadStock());
                checkStockEntity.setAfterAmount(productStock.getBadStock() + stockCheckDTO.getSkuQuantity());
                // 批次盘点
                Map<Long, Integer> batchAmountMap = batchBizService.batchUpdate(stockCheckDTO.getUserId(),
                        stockCheckDTO.getSkuId(), stockCheckDTO.getStashName(), ProductType.BAD, stockCheckDTO.getSkuQuantity());
                for (Map.Entry<Long, Integer> entry : batchAmountMap.entrySet()) {
                    long batchId = entry.getKey();
                    int amount = entry.getValue();
                    PhysicalStockFlowEntity target = new PhysicalStockFlowEntity();
                    QtBeanUtil.copyProperties(srcFlow, target);
                    target.setBeforeAmount(srcBadStock);
                    target.setAmount(amount);
                    target.setAfterAmount(srcBadStock + amount);
                    target.setBatchId(batchId);
                    flows.add(target);
                    srcBadStock = srcBadStock + amount;
                }
                productStock.setBadStock(srcBadStock);
                // 实物流水记录
            } else {
                if (skuQuantity < 0 && Math.abs(skuQuantity) > productStock.getGoodStock()) {
                    log.warn("库存盘点：盘亏正品({})超过剩余实际({})的正品数，请核对", skuQuantity, productStock.getGoodStock());
                    throw ExceptionFactory.getI18nBizException("stock.check.error");
                }

                if (skuQuantity < 0) {
                    int available = productStock.getGoodStock() - productStock.getPreemptionStock() - productStock.getFrozenStock();
                    if (available < Math.abs(skuQuantity)) {  // 无法直接减，需要扣除预占
                        outAmount = Math.abs(skuQuantity) - available;
                        // 取消预占
                        List<String> releaseBusinessNo = preemptionStockBizService.releasePreemption(BusinessType.STOCK_CHECK,
                                stockCheckDTO.getUserId(), stockCheckDTO.getSkuId(), stockCheckDTO.getStashName(),
                                stockCheckDTO.getBusinessNo(), Math.abs(skuQuantity) - available);
                        // 通知订单
                        OrderEntity updateStatus = new OrderEntity();
                        updateStatus.setCustomerOrderStatus(CustomerOrderStatusEnum.OUT_OF_STOCK);
                        updateStatus.setEmployeeOrderStatus(EmployeeOrderStatusEnum.OUT_OF_STOCK);
                        updateStatus.setOrderStockStatus(OrderStockStatusEnum.OUT_OF_STOCK);
                        orderService.updateOrderInfos(releaseBusinessNo, updateStatus);
                    }
                }
                int srcGoodStock = productStock.getGoodStock();
                checkStockEntity.setBeforeAmount(productStock.getGoodStock());
                checkStockEntity.setAfterAmount(productStock.getGoodStock() + stockCheckDTO.getSkuQuantity());
                Map<Long, Integer> batchAmountMap = batchBizService.batchUpdate(stockCheckDTO.getUserId(),
                        stockCheckDTO.getSkuId(), stockCheckDTO.getStashName(), ProductType.GOOD, stockCheckDTO.getSkuQuantity());
                for (Map.Entry<Long, Integer> entry : batchAmountMap.entrySet()) {
                    long batchId = entry.getKey();
                    int amount = entry.getValue();
                    PhysicalStockFlowEntity target = new PhysicalStockFlowEntity();
                    QtBeanUtil.copyProperties(srcFlow, target);
                    target.setBeforeAmount(srcGoodStock);
                    target.setAmount(amount);
                    target.setAfterAmount(srcGoodStock + amount);
                    target.setBatchId(batchId);
                    flows.add(target);
                    srcGoodStock = srcGoodStock + amount;
                }
                productStock.setGoodStock(srcGoodStock);
            }
            productStockService.updateById(productStock);
            physicalStockFlowService.saveBatch(flows);
            checkStockMapper.insert(checkStockEntity);

            if (outAmount > 0) {
                // 缺货变化
                outStockBizService.addStock(stockCheckDTO.getUserId(),  stockCheckDTO.getSkuId(),
                        stockCheckDTO.getStashName(), outAmount);
            }
            if (productType == ProductType.GOOD && stockCheckDTO.getSkuQuantity() > 0) {
                // 缺货变化
                outStockBizService.minusStock(stockCheckDTO.getUserId(),  stockCheckDTO.getSkuId(),
                        stockCheckDTO.getStashName(), stockCheckDTO.getSkuQuantity());
            }
        } finally {
            distributedLocker.unlock(lockKey);
        }

    }

    @Transactional(rollbackFor = Exception.class)
    public void wmsStockTransfer(StockTransferDTO stockTransferDTO) {
        log.info("库存管理-库存转移处理-开始：{}", QtFastjsonUtil.toJSONString(stockTransferDTO));
        if (stockTransferDTO.getSkuQuantity() <= 0) {
            log.info("库存转移：变更数量为0，不需要做任何操作");
            return;
        }

        String lockKey = CacheKeyAssist.getPhysicalStockUpdatedLockKey(stockTransferDTO.getUserId(),
                stockTransferDTO.getSkuId(), stockTransferDTO.getStashName());
        if (!distributedLocker.tryLock(lockKey)) {
            log.error("库存转移更新实物库存时获取锁失败");
            throw ExceptionFactory.getI18nBizException("stock.physical.updated.concurrence.error");
        }

        try {
            TransferStockEntity transferStockEntity = new TransferStockEntity();
            QtBeanUtil.copyProperties(stockTransferDTO, transferStockEntity);
            transferStockEntity.setAmount(stockTransferDTO.getSkuQuantity());

            PhysicalStockFlowEntity srcFlow = new PhysicalStockFlowEntity();
            srcFlow.setBusinessType(BusinessType.STOCK_TRANSFER.getCode());
            srcFlow.setUserId(stockTransferDTO.getUserId());
            srcFlow.setSkuId(stockTransferDTO.getSkuId());
            srcFlow.setStashName(stockTransferDTO.getStashName());
            srcFlow.setBusinessNo(stockTransferDTO.getBusinessNo());
            List<PhysicalStockFlowEntity> flows = Lists.newArrayList();

            ProductStockEntity productStock = productStockService.get(stockTransferDTO.getUserId(),
                    stockTransferDTO.getStashName(), stockTransferDTO.getSkuId());
            ProductType sourceProductType = IEnumConvert.valueOf(stockTransferDTO.getSourceType(), ProductType.class);
            ProductType targetProductType = IEnumConvert.valueOf(stockTransferDTO.getTargetType(), ProductType.class);
            transferStockEntity.setSourceType(sourceProductType.getValue());
            transferStockEntity.setTargetType(targetProductType.getValue());
            int outAmount = 0;
            if (sourceProductType == ProductType.GOOD && targetProductType == ProductType.BAD) { // 好的变坏了，好的减少了
                int skuQuantity = stockTransferDTO.getSkuQuantity();
                if (skuQuantity > productStock.getGoodStock()) {
                    log.warn("库存转移：转移正品({})超过剩余实际({})的正品数，请核对", skuQuantity, productStock.getGoodStock());
                    throw ExceptionFactory.getI18nBizException("stock.check.error");
                }

                int available = productStock.getGoodStock() - productStock.getPreemptionStock() - productStock.getFrozenStock();
                if (available < skuQuantity) {  // 无法直接减，需要扣除预占
                    outAmount = Math.abs(skuQuantity) - available;
                    // 取消预占
                    List<String> releaseBusinessNo = preemptionStockBizService.releasePreemption(BusinessType.STOCK_TRANSFER,
                            stockTransferDTO.getUserId(), stockTransferDTO.getSkuId(), stockTransferDTO.getStashName(),
                            stockTransferDTO.getBusinessNo(), skuQuantity - available);
                    // 通知订单
                    OrderEntity updateStatus = new OrderEntity();
                    updateStatus.setCustomerOrderStatus(CustomerOrderStatusEnum.OUT_OF_STOCK);
                    updateStatus.setEmployeeOrderStatus(EmployeeOrderStatusEnum.OUT_OF_STOCK);
                    updateStatus.setOrderStockStatus(OrderStockStatusEnum.OUT_OF_STOCK);
                    orderService.updateOrderInfos(releaseBusinessNo, updateStatus);
                }

                int srcGoodStock = productStock.getGoodStock();
                transferStockEntity.setBeforeGoodAmount(productStock.getGoodStock());
                transferStockEntity.setBeforeBadAmount(productStock.getBadStock());
                transferStockEntity.setAfterGoodAmount(productStock.getGoodStock() - skuQuantity);
                transferStockEntity.setAfterBadAmount(productStock.getBadStock() + skuQuantity);
                Map<Long, Integer> batchAmountMap = batchBizService.batchUpdate(stockTransferDTO.getUserId(),
                        stockTransferDTO.getSkuId(), stockTransferDTO.getStashName(), ProductType.GOOD, -skuQuantity);
                for (Map.Entry<Long, Integer> entry : batchAmountMap.entrySet()) {
                    long batchId = entry.getKey();
                    int amount = entry.getValue();
                    PhysicalStockFlowEntity target = new PhysicalStockFlowEntity();
                    QtBeanUtil.copyProperties(srcFlow, target);
                    target.setBeforeAmount(srcGoodStock);
                    target.setAmount(amount);
                    target.setType(ProductType.GOOD.getValue());
                    target.setAfterAmount(srcGoodStock + amount);
                    target.setBatchId(batchId);
                    flows.add(target);
                    srcGoodStock = srcGoodStock + amount;
                }
                productStock.setGoodStock(srcGoodStock);

                int srcBadStock = productStock.getBadStock();
                batchAmountMap = batchBizService.batchUpdate(stockTransferDTO.getUserId(),
                        stockTransferDTO.getSkuId(), stockTransferDTO.getStashName(), ProductType.BAD, skuQuantity);
                for (Map.Entry<Long, Integer> entry : batchAmountMap.entrySet()) {
                    long batchId = entry.getKey();
                    int amount = entry.getValue();
                    PhysicalStockFlowEntity target = new PhysicalStockFlowEntity();
                    QtBeanUtil.copyProperties(srcFlow, target);
                    target.setBeforeAmount(srcBadStock);
                    target.setAmount(amount);
                    target.setType(ProductType.BAD.getValue());
                    target.setAfterAmount(srcBadStock + amount);
                    target.setBatchId(batchId);
                    flows.add(target);
                    srcBadStock = srcBadStock + amount;
                }
                productStock.setBadStock(srcBadStock);
            } else if (sourceProductType == ProductType.BAD && targetProductType == ProductType.GOOD) {
                int skuQuantity = stockTransferDTO.getSkuQuantity();
                if (skuQuantity > productStock.getBadStock()) {
                    log.warn("库存转移：转移次品({})超过剩余实际({})的次品数，请核对", skuQuantity, productStock.getBadStock());
                    throw ExceptionFactory.getI18nBizException("stock.check.error");
                }

                transferStockEntity.setBeforeGoodAmount(productStock.getGoodStock());
                transferStockEntity.setBeforeBadAmount(productStock.getBadStock());
                transferStockEntity.setAfterGoodAmount(productStock.getGoodStock() + skuQuantity);
                transferStockEntity.setAfterBadAmount(productStock.getBadStock() - skuQuantity);

                int srcGoodStock = productStock.getGoodStock();
                transferStockEntity.setBeforeGoodAmount(productStock.getGoodStock());
                transferStockEntity.setAfterGoodAmount(productStock.getGoodStock() - skuQuantity);
                Map<Long, Integer> batchAmountMap = batchBizService.batchUpdate(stockTransferDTO.getUserId(),
                        stockTransferDTO.getSkuId(), stockTransferDTO.getStashName(), ProductType.GOOD, skuQuantity);
                for (Map.Entry<Long, Integer> entry : batchAmountMap.entrySet()) {
                    long batchId = entry.getKey();
                    int amount = entry.getValue();
                    PhysicalStockFlowEntity target = new PhysicalStockFlowEntity();
                    QtBeanUtil.copyProperties(srcFlow, target);
                    target.setBeforeAmount(srcGoodStock);
                    target.setAmount(amount);
                    target.setType(ProductType.GOOD.getValue());
                    target.setAfterAmount(srcGoodStock + amount);
                    target.setBatchId(batchId);
                    flows.add(target);
                    srcGoodStock = srcGoodStock + amount;
                }
                productStock.setGoodStock(srcGoodStock);

                int srcBadStock = productStock.getBadStock();
                batchAmountMap = batchBizService.batchUpdate(stockTransferDTO.getUserId(),
                        stockTransferDTO.getSkuId(), stockTransferDTO.getStashName(), ProductType.BAD, -skuQuantity);
                for (Map.Entry<Long, Integer> entry : batchAmountMap.entrySet()) {
                    long batchId = entry.getKey();
                    int amount = entry.getValue();
                    PhysicalStockFlowEntity target = new PhysicalStockFlowEntity();
                    QtBeanUtil.copyProperties(srcFlow, target);
                    target.setBeforeAmount(srcBadStock);
                    target.setAmount(amount);
                    target.setType(ProductType.BAD.getValue());
                    target.setAfterAmount(srcBadStock + amount);
                    target.setBatchId(batchId);
                    flows.add(target);
                    srcBadStock = srcBadStock + amount;
                }
                productStock.setBadStock(srcBadStock);
            } else {
                log.warn("库存管理-库存处理处理-转移逻辑异常：source：{}，target：{}", sourceProductType, targetProductType);
                return;
            }

            transferStockMapper.insert(transferStockEntity);
            productStockService.updateById(productStock);
            physicalStockFlowService.saveBatch(flows);
            if (outAmount > 0) {
                outStockBizService.addStock(stockTransferDTO.getUserId(),  stockTransferDTO.getSkuId(),
                        stockTransferDTO.getStashName(), outAmount);
            }
            if (targetProductType == ProductType.GOOD) {
                // 缺货变化
                outStockBizService.minusStock(stockTransferDTO.getUserId(),  stockTransferDTO.getSkuId(),
                        stockTransferDTO.getStashName(), stockTransferDTO.getSkuQuantity());
            }
        } finally {
            distributedLocker.unlock(lockKey);
        }

        // target 库存加
        log.info("库存管理-库存转移处理-实物库存-结束");
    }
}
